﻿using System;
using System.Windows.Forms;
using DotNetty.Buffers;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System.Text;

using NewLife.Data;
using NewLife.Net.Modbus1;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using Elastic.Clients.Elasticsearch.MachineLearning;
using WinFormsApp1;
using System.Collections;
using System.Net;

namespace WinFormsApp1
{
    public partial class Form2 : Form
    {
        private IChannel serverChannel;

        public Form2()
        {
            InitializeComponent();
        }

        private async void buttonStartServer_Click(object sender, EventArgs e)
        {
            var bossGroup = new MultithreadEventLoopGroup(1);
            var workerGroup = new MultithreadEventLoopGroup();

            try
            {
                var bootstrap = new ServerBootstrap();
                bootstrap.Group(bossGroup, workerGroup)
                         .Channel<TcpServerSocketChannel>()
                         .Option(ChannelOption.SoBacklog, 100)
                         .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
                         {
                             IChannelPipeline pipeline = channel.Pipeline;
                             pipeline.AddLast(new ServerHandler(this));
                         }));

                // serverChannel = await bootstrap.BindAsync(502);

                var ipAddress = IPAddress.Parse("192.168.0.104"); // 替换为你想要监听的 IP 地址
                var port = 502;
                serverChannel = await bootstrap.BindAsync(new IPEndPoint(ipAddress, port));
                Log("Server started on port 502");
            }
            catch (Exception ex)
            {
                Log($"Exception: {ex.Message}");
            }
        }

        public void Log(string message)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<string>(Log), message);
            }
            else
            {
                textBoxLog.AppendText($"{message}{Environment.NewLine}");
            }
        }
    }

    public class ServerHandler : ChannelHandlerAdapter
    {
        private readonly Form2 form;
        public static List<ModbusConnectInfo> ModbusConnectInfos = new List<ModbusConnectInfo>();

        public ServerHandler(Form2 form)
        {
            this.form = form;
        }

        public override void ChannelActive(IChannelHandlerContext context)
        {
            form.Log("Client connected");
            base.ChannelActive(context);
            if (!ModbusConnectInfos.Any(u => u.ChannelHandlerContext == context))
            {
                ModbusConnectInfos.Add(new ModbusConnectInfo { ChannelHandlerContext = context });
            }
        }

        public override void ChannelInactive(IChannelHandlerContext context)
        {
            form.Log("Client disconnected");
            base.ChannelInactive(context);
            ModbusConnectInfos.RemoveAll(u => u.ChannelHandlerContext == context);
        }

        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            var byteBuffer = message as IByteBuffer;
            byte[] receivedBytes = new byte[byteBuffer.ReadableBytes];
            byteBuffer.GetBytes(byteBuffer.ReaderIndex, receivedBytes);

            var info = ModbusConnectInfos.FirstOrDefault(u => u.ChannelHandlerContext == context);
            if (info == null)
            {
                return;
            }

            var pk = new Packet(receivedBytes);
            var rs = new ModbusEntity();
            rs = rs.Parse(pk.Data, pk.Offset, pk.Count);
            info.MyMaster.ModbusEntity = rs;
            var host = rs.Host;
            //数据主动上报
            if ((int)info.MyMaster.ModbusEntity.Function > 0X40)
            {
                // var re = info.MyMaster.ReadCoil(1);
                var re = info.MyMaster.ProcessForReport(info.MyMaster.ModbusEntity, info.MyMaster.ModbusEntity.Data.Length);
            }
            else
            {
                //操控从机指令
                info.MyMaster.IsReplied = true;
                info.MyMaster.ModbusEntity = rs;
            }

            if (byteBuffer != null)
            {
                string receivedMessage = byteBuffer.ToString(Encoding.UTF8);
                form.Log($"Received: {receivedMessage}");
                // context.WriteAndFlushAsync(Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes("Message received")));
            }
        }

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            form.Log($"Exception: {exception.Message}");
            context.CloseAsync();
        }
    }
}

public class ModbusConnectInfo
{
    public string Ip { get; set; }
    public int Port { get; set; }
    public string Sn { get; set; }
    public string Pwd { get; set; }

    private IChannelHandlerContext channelHandlerContext;

    public IChannelHandlerContext ChannelHandlerContext
    {
        get { return channelHandlerContext; }
        set { channelHandlerContext = value; MyMaster.ChannelHandlerContext = channelHandlerContext; }
    }

    /// <summary>
    ///
    /// </summary>
    public MyMaster MyMaster = new MyMaster();
}

public class MyMaster
{
    public MyMaster()
    {
    }

    /// <summary>主机地址。用于485编码</summary>
    public Byte Host { get; set; } = 1;

    /// <summary>发送数据后接收数据前的延迟时间，默认0毫秒</summary>
    public Int32 Delay { get; set; }

    public IChannelHandlerContext ChannelHandlerContext { get; set; }
    public bool IsReplied { get; set; }

    public ModbusEntity ModbusEntity { get; set; }

    #region 方法

    /// <summary>处理指令</summary>
    /// <param name="entity">指令实体</param>
    /// <param name="expect">预期返回数据长度</param>
    /// <returns></returns>
    private ModbusEntity Process(ModbusEntity entity, Int32 expect)
    {
        if (ChannelHandlerContext == null) throw new NullReferenceException("ChannelHandlerContext");

        entity.Host = Host;

        // 发送
        var buf = entity.ToArray();

        // Modbus加锁，防止冲突
        lock (this)
        {
            IsReplied = false;
            ModbusEntity = null;
            // 预期返回指令长度，传入参数expect没有考虑头部和校验位
            //var st = Transport as SerialTransport;
            //if (st != null) st.FrameSize = expect + ModbusEntity.NO_DATA_LENGTH;
            AsyncToSync.RunSync(() => { return ChannelHandlerContext.WriteAndFlushAsync(Unpooled.CopiedBuffer(buf)); });

            // lscy 2013-7-29
            // 发送后，休眠一段时间，避免设备数据未全部写到串口缓冲区中
            // 一般情况下，100ms 已足够
            //延时100ms看是否返回
            var dt = DateTime.Now;
            while (!IsReplied)
            {
                Thread.Sleep(5);
                if (DateTime.Now.Subtract(dt).TotalMilliseconds > 200)
                    throw new Exception("通讯超时");
            }

            // 读取
            //var pk = Transport.Receive();
            //if (pk == null || pk.Count == 0) return null;

            var rs = ModbusEntity;
            if (rs == null) return null;
            if (rs.IsException) throw new ModbusException(rs.Data != null && rs.Data.Length > 0 ? (Errors)rs.Data[0] : 0);

            return rs;
        }
    }

    /// <summary>处理指令</summary>
    /// <param name="entity">指令实体</param>
    /// <param name="expect">预期返回数据长度</param>
    /// <returns></returns>
    public ModbusEntity ProcessForReport(ModbusEntity entity, Int32 expect)
    {
        if (ChannelHandlerContext == null) throw new NullReferenceException("ChannelHandlerContext");

        //entity.Host = Host;

        // 发送
        var buf = entity.ToArray();

        // Modbus加锁，防止冲突
        lock (this)
        {
            //回答
            AsyncToSync.RunSync(() =>
            {
                return ChannelHandlerContext.WriteAndFlushAsync(Unpooled.CopiedBuffer(GetReportResponse(entity)));
            });

            var pk = new Packet(new byte[0]);

            var rs = new ModbusEntity();
            rs = rs.Parse(pk.Data, pk.Offset, pk.Count);
            if (rs == null) return null;
            if (rs.IsException) throw new ModbusException(rs.Data != null && rs.Data.Length > 0 ? (Errors)rs.Data[0] : 0);

            return rs;
        }
    }

    #endregion 方法

    #region 线圈

    /// <summary>读取线圈状态</summary>
    /// <remarks>
    /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
    /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
    /// </remarks>
    /// <param name="addr"></param>
    /// <returns></returns>
    public Boolean ReadCoil(Int32 addr)
    {
        // 小心内部可能没有返回
        var rs = ReadInputs(MBFunction.ReadCoils, addr, 1);
        if (rs == null || rs.Length < 1) return false;
        return rs[0];
    }

    /// <summary>读取线圈状态</summary>
    /// <remarks>
    /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
    /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public Boolean[] ReadCoils(Int32 addr, UInt16 count)
    {
        return ReadInputs(MBFunction.ReadCoils, addr, count);
    }

    /// <summary>读取离散量输入</summary>
    /// <remarks>
    /// 请求：0x02|2字节起始地址|2字节输入数量(1~2000)
    /// 响应：0x02|1字节字节计数|n字节输入状态（n=输入数量/8，如果余数不为0，n=n+1）
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public Boolean[] ReadInputs(Int32 addr, UInt16 count)
    {
        return ReadInputs(MBFunction.ReadInputs, addr, count);
    }

    private Boolean[] ReadInputs(MBFunction func, Int32 addr, UInt16 count)
    {
        var cmd = new ModbusEntity
        {
            Function = func
        };
        var buf = new Byte[4];
        buf.WriteUInt16(0, addr);
        buf.WriteUInt16(2, count);
        cmd.Data = buf;

        var rLen = 1 + count / 8;
        if (count % 8 != 0) rLen++;
        var rs = Process(cmd, rLen);
        if (rs == null || rs.Data == null || rs.Data.Length < 1) return null;

        // 特殊处理单个读取，提高效率
        if (count == 1) return new Boolean[] { rs.Data[1] == 1 };

        var flags = new Boolean[count];

        // 元素存放于m字节n位
        Int32 m = 0, n = 0;
        for (var i = 0; i < flags.Length && 1 + m < rs.Data.Length; i++)
        {
            if (((rs.Data[1 + m] >> n) & 0x01) == 1) flags[i] = true;
            if (++n >= 8)
            {
                m++;
                n = 0;
            }
        }

        return flags;
    }

    /// <summary>写单个线圈</summary>
    /// <remarks>
    /// 请求：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
    /// 响应：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="flag"></param>
    /// <returns></returns>
    public Boolean WriteSingleCoil(Int32 addr, Boolean flag)
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.WriteSingleCoil
        };
        var buf = new Byte[4];
        buf.WriteUInt16(0, addr);
        if (flag) buf.WriteUInt16(2, 0xFF00);
        cmd.Data = buf;

        var rs = Process(cmd, 2 + 2);
        if (rs == null) return false; ;

        return (rs.Data.ReadUInt16(2) != 0) == flag;
    }

    /// <summary>写多个线圈</summary>
    /// <remarks>
    /// 请求：0x0F|2字节起始地址|2字节输出数量（1~1698）|1字节字节计数|n字节输出值（n=输出数量/8，如果余数不为0，n=n+1）
    /// 响应：0x0F|2字节起始地址|2字节输出数量
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="flags"></param>
    /// <returns></returns>
    public Boolean WriteMultipleCoils(Int32 addr, params Boolean[] flags)
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.WriteMultipleCoils
        };

        var n = flags.Length / 8;
        if (flags.Length % 8 != 0) n++;

        var buf = new Byte[4 + 1 + n];
        buf.WriteUInt16(0, addr);
        buf.WriteUInt16(2, (UInt16)flags.Length);

        buf[4] = (Byte)n;

        // 元素存放于m字节n位
        var m = n = 0;
        for (var i = 0; i < flags.Length; i++)
        {
            if (flags[i]) buf[5 + m] |= (Byte)(1 << n);

            if (++n >= 8)
            {
                m++;
                n = 0;
            }
        }

        cmd.Data = buf;

        var rs = Process(cmd, 2 + 2);
        if (rs == null) return false;

        return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == flags.Length;
    }

    #endregion 线圈

    #region 寄存器

    /// <summary>读取保持寄存器</summary>
    /// <remarks>
    /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
    /// 响应：0x03|1字节字节数|n*2字节寄存器值
    /// </remarks>
    /// <param name="addr"></param>
    /// <returns></returns>
    public UInt16 ReadHoldingRegister(Int32 addr)
    {
        return ReadRegisters(MBFunction.ReadHoldingRegisters, addr, 1)[0];
    }

    /// <summary>读取保持寄存器</summary>
    /// <remarks>
    /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
    /// 响应：0x03|1字节字节数|n*2字节寄存器值
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public UInt16[] ReadHoldingRegisters(Int32 addr, UInt16 count)
    {
        return ReadRegisters(MBFunction.ReadHoldingRegisters, addr, count);
    }

    /// <summary>读取输入寄存器</summary>
    /// <remarks>
    /// 请求：0x04|2字节起始地址|2字节输入寄存器数量（1~2000）
    /// 响应：0x04|1字节字节数|n*2字节输入寄存器
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public UInt16[] ReadInputRegisters(Int32 addr, UInt16 count)
    {
        return ReadRegisters(MBFunction.ReadInputRegisters, addr, count);
    }

    private UInt16[] ReadRegisters(MBFunction func, Int32 addr, UInt16 count)
    {
        var cmd = new ModbusEntity
        {
            Function = func
        };
        var buf = new Byte[4];
        buf.WriteUInt16(0, addr);
        buf.WriteUInt16(2, count);
        cmd.Data = buf;

        var rs = Process(cmd, 1 + count * 2);
        if (rs == null) return null;

        count = rs.Data[0];
        if (1 + count > rs.Data.Length) count = (UInt16)(rs.Data.Length - 1);

        var ds = new UInt16[count / 2];
        for (var i = 0; i < ds.Length; i++)
        {
            ds[i] = rs.Data.ReadUInt16(1 + i * 2);
        }

        return ds;
    }

    /// <summary>写单个寄存器</summary>
    /// <remarks>
    /// 请求：0x06|2字节寄存器地址|2字节寄存器值
    /// 响应：0x06|2字节寄存器地址|2字节寄存器值
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="val"></param>
    /// <returns></returns>
    public Boolean WriteSingleRegister(Int32 addr, UInt16 val)
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.WriteSingleRegister
        };
        var buf = new Byte[4];
        buf.WriteUInt16(0, addr);
        buf.WriteUInt16(2, val);
        cmd.Data = buf;

        var rs = Process(cmd, 2 + 2);
        if (rs == null) return false;

        return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == val;
    }

    /// <summary>写多个寄存器</summary>
    /// <remarks>
    /// 请求：0x10|2字节起始地址|2字节寄存器数量（1~123）|1字节字节计数|n*2寄存器值
    /// 响应：0x10|2字节起始地址|2字节寄存器数量
    /// </remarks>
    /// <param name="addr"></param>
    /// <param name="vals"></param>
    /// <returns></returns>
    public Boolean WriteMultipleRegisters(Int32 addr, params UInt16[] vals)
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.WriteMultipleRegisters
        };

        var buf = new Byte[4 + 1 + vals.Length * 2];
        buf.WriteUInt16(0, addr);
        buf.WriteUInt16(2, (UInt16)vals.Length);
        // 字节计数
        buf[4] = (Byte)(vals.Length * 2);

        for (var i = 0; i < vals.Length; i++)
        {
            buf.WriteUInt16(5 + i * 2, vals[i]);
        }

        cmd.Data = buf;

        var rs = Process(cmd, 2 + 2);
        if (rs == null) return false;

        return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == vals.Length;
    }

    #endregion 寄存器

    #region 诊断标识

    /// <summary>诊断</summary>
    /// <remarks>
    /// 01080000801A
    /// 请求：0x08|2字节子功能|n*2字节数据
    /// 响应：0x08|2字节子功能|n*2字节数据
    /// </remarks>
    /// <returns></returns>
    public Boolean Diagnostics()
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.Diagnostics
        };

        // 子功能码
        var buf = new Byte[2];
        buf.WriteUInt16(0, 0);
        cmd.Data = buf;

        var rs = Process(cmd, 2);
        return rs != null;
    }

    /// <summary>返回标识</summary>
    /// <remarks>
    /// 0111C02C
    /// </remarks>
    /// <returns></returns>
    public Byte[] ReportIdentity()
    {
        var cmd = new ModbusEntity
        {
            Function = MBFunction.ReportIdentity
        };

        var rs = Process(cmd, 1 + 8);
        if (rs == null) return null;

        var count = (Int32)rs.Data[0];
        if (count > rs.Data.Length - 1) count = rs.Data.Length - 1;

        if (count <= 0) return new Byte[0];

        return rs.Data.ReadBytes(1, count);
    }

    #endregion 诊断标识

    #region 主动上传

    public byte[] GetReportResponse(ModbusEntity modbusEntity)
    {
        var func = modbusEntity.Function;
        var cmd = new ModbusEntity
        {
            Function = func
        };
        cmd.Data = modbusEntity.Data.Take(4).ToArray();
        return cmd.ToArray();
    }

    public Tuple<Int32, Int32, Boolean[]> ReadCoils(ModbusEntity rs)
    {
        if (rs == null || rs.Data == null || rs.Data.Length < 1) return null;

        // 将byte数组转成int

        var addr = BitConverter.ToInt32(rs.Data.Take(2).ToArray(), 0);

        var count = BitConverter.ToInt32(rs.Data.Skip(2).Take(2).ToArray(), 0);

        // 特殊处理单个读取，提高效率
        if (count == 1) return Tuple.Create(addr, count, new Boolean[] { rs.Data[1] == 1 });

        var flags = new Boolean[count];

        // 元素存放于m字节n位
        Int32 m = 0, n = 0;
        for (var i = 0; i < flags.Length && 1 + m < rs.Data.Length; i++)
        {
            if (((rs.Data[1 + m] >> n) & 0x01) == 1) flags[i] = true;
            if (++n >= 8)
            {
                m++;
                n = 0;
            }
        }
        return Tuple.Create(addr, count, flags);
    }

    public Tuple<Int32, Int32, Boolean[]> ReadInputs(ModbusEntity rs)
    {
        return ReadCoils(rs);
    }

    public Tuple<Int32, Int32, UInt16[]> ReadRegisters(ModbusEntity rs)
    {
        if (rs == null) return null;

        // 将byte数组转成int

        var addr = BitConverter.ToInt32(rs.Data.Take(2).ToArray(), 0);

        var count = BitConverter.ToInt32(rs.Data.Skip(2).Take(2).ToArray(), 0);

        if (1 + count > rs.Data.Length) count = (UInt16)(rs.Data.Length - 1);

        var ds = new UInt16[count / 2];
        for (var i = 0; i < ds.Length; i++)
        {
            ds[i] = rs.Data.ReadUInt16(1 + i * 2);
        }

        return Tuple.Create(addr, count, ds);
    }

    #endregion 主动上传
}